#include "bctoolbox/vfs.h"
#include "belle-sip/utils.h"
#include "linphone/account_creator.h"
#include "linphone/api/c-account-params.h"
#include "linphone/api/c-account.h"
#include "linphone/api/c-address.h"
#include "linphone/api/c-auth-info.h"
#include "linphone/call.h"
#include "linphone/core.h"
#include "log/ohos_log.h"
#include "napi/native_api.h"
#include <belr/grammarbuilder.h>
#include <iostream>
#include <linphone/core.h>
#include <thread>

//-----
#include "unwrap/build_class.h"
// 全局标记位：是否有来电
static  bool incoming_call = false;
static LinphoneCall *current_call = nullptr;

struct LinphoneData {
    LinphoneCore *lc;
    LinphoneAccount *account;
    LinphoneAuthInfo *auth_info;
};

// 注册状态变化回调函数
static void registration_state_changed(LinphoneCore *core, LinphoneAccount *account, LinphoneRegistrationState state,
                                       const char *message) {
    switch (state) {
    case LinphoneRegistrationNone:
        LOGE("linphoneTag messageCallBack未初始化");
        break;
    case LinphoneRegistrationProgress:
        LOGE("linphoneTag messageCallBack正在注册...");
        break;
    case LinphoneRegistrationOk:
        LOGE("linphoneTag messageCallBack注册成功!");
        break;
    case LinphoneRegistrationCleared:
        LOGE("linphoneTag messageCallBack已注销");
        break;
    case LinphoneRegistrationFailed:
        LOGE("linphoneTag messageCallBack注册失败");
        break;
    default:
        LOGE("linphoneTag messageCallBack未知状态");
    }
}

static void log_handler(const char *domain, BctbxLogLevel level, const char *message, va_list args) { LOGL(args, message); }
// 回调函数处理呼叫状态
static void call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *msg) {
    switch (cstate) {
    case LinphoneCallIncomingReceived:
        LOGE("linphoneTag2 通话已接通,来电号码: %s",
             linphone_address_get_username(linphone_call_get_remote_address(call)));
        incoming_call = true;
        current_call = call;
        break;
    case LinphoneCallStreamsRunning: {
        MSFactory *factory = linphone_core_get_ms_factory(lc);
        MSFilter *custom_filter = ms_factory_create_filter(factory, MS_OHOS_AUDIO_ENC);
        if (custom_filter) {
            // 获取音频流
            
//                LinphoneMediaStream *stream = linphone_call_get_media_stream(call, LinphoneMediaAudio);
//                if (stream) {
//                    // 获取当前音频处理链
//                    MSFilter *audio_chain = linphone_media_stream_get_filter(stream);
            //                    
//                    // 插入到发送链
//                    ms_filter_link(ms_filter_get_source(audio_chain), 0, custom_filter, 0);
//                    ms_filter_link(custom_filter, 0, ms_filter_get_sink(audio_chain), 0);
            //                    
//                    // 设置自定义参数
//                    my_audio_filter_set_param(custom_filter, 42);
            //                    
//                    printf("Custom audio filter activated!\n");
            
//                }
            // ms_filter_link  //ms_filter_get_sink
//             ms_filter_link(MSFilter *f1, int pin1, MSFilter *f2, int pin2)
        }
    }

    break;
    case LinphoneCallOutgoingInit:
        LOGE("linphoneTag2 正在发起呼叫...");
        break;
    case LinphoneCallOutgoingProgress:
        LOGE("linphoneTag2 呼叫进行中...");
        break;
    case LinphoneCallConnected:
        LOGE("linphoneTag2 通话已连接");
        break;
    case LinphoneCallEnd:
        LOGE("linphoneTag2 通话结束");
        if (current_call) {
            linphone_call_unref(current_call);
            current_call = nullptr;
        }
        break;
    case LinphoneCallError:
        LOGE("linphoneTag2 呼叫错误: %s", msg);
        break;
    default:
        break;
    }
}

extern MSFilterDesc ms_ohos_audio_enc_desc;
extern MSFilterDesc ms_ohos_audio_dec_desc;
extern MSSndCardDesc ohos_snd_card_desc;

static void ms_ohos_snd_card_register(MSFactory *factory) {
    ms_message("Registering OHOS virtual audio card...");

    // 注册自定义的 encoder/decoder
    ms_factory_register_filter(factory, &ms_ohos_audio_enc_desc);
    ms_factory_register_filter(factory, &ms_ohos_audio_dec_desc);

    // 创建并注册设备
    MSSndCard *card = ms_snd_card_new(&ohos_snd_card_desc);
    ms_snd_card_set_name(card, "OHOS Virtual Audio");

    MSSndCardManager *manager = ms_factory_get_snd_card_manager(factory);
    ms_snd_card_manager_add_card(manager, card);
}

LinphoneData login(std::string path) {
    
    belr::GrammarLoader::get().addPath(path);
    linphone_factory_set_data_dir(linphone_factory_get(), path.c_str());
    
    
    LOGE("linphoneTag 开始登录");
    LinphoneData linphoneData;
    LinphoneCoreVTable vtable = {0};
    linphone_core_set_log_level(BCTBX_LOG_DEBUG);
    linphone_core_set_log_handler(log_handler);
    
    vtable.account_registration_state_changed = registration_state_changed;
    vtable.call_state_changed = call_state_changed;
    LinphoneCore *lc = linphone_core_new(&vtable, NULL, NULL, NULL);
    
    MSFactory *factory = linphone_core_get_ms_factory(lc);
    ms_ohos_snd_card_register(factory);
    
    bctbx_list_t * list = linphone_core_get_audio_devices(lc);
    int audio_devices_count = (int)bctbx_list_size(list);
    LOGE("%s: %d audio_devices_count: %d", __FUNCTION__, __LINE__, audio_devices_count);
    
    // 禁用默认音频设备（让自定义编码器处理）
    linphone_core_set_capture_device(lc, nullptr);
    linphone_core_set_playback_device(lc, nullptr);
        
    linphoneData.lc = lc;
     //这两个配置无效 目前在底层写死
    // 配置认证信息
    LinphoneAuthInfo *auth_info = linphone_auth_info_new("1006", nullptr, "1006", nullptr, nullptr, nullptr);
    linphoneData.auth_info = auth_info;
    linphone_core_add_auth_info(lc, auth_info);

    // 创建账户参数（添加错误检查）
    LinphoneAddress *identity_addr = linphone_address_new("sip:1006@47.96.177.62:5060");
    if (!identity_addr) {
        LOGE("linphoneTag 无效的Identity地址!");
        linphone_address_unref(identity_addr);
        return linphoneData;
    }
    LinphoneAddress *server_addr = linphone_address_new("sip:47.96.177.62:5060;transport=udp");
    if (!server_addr) {
        LOGE("linphoneTag 无效的服务器地址!");
        linphone_address_unref(identity_addr);
        return linphoneData;
    }
    LinphoneAccountParams *account_params = linphone_account_params_new(nullptr, FALSE);
    linphone_account_params_set_identity_address(account_params, identity_addr);
    linphone_account_params_set_server_address(account_params, server_addr);
    linphone_account_params_enable_register(account_params, TRUE);

    LinphoneAccount *account = linphone_core_create_account(lc, account_params);
    linphoneData.account = account;
    if (!account) {
        LOGE("linphoneTag 账户添加失败!");
        return linphoneData;
    }
    int rv = linphone_core_add_account(lc, account);
    linphone_core_set_default_account(lc, account);
    LOGE("linphoneTag 账户添加linphone_core_add_account %d", rv);
    // 释放临时对象
    linphone_address_unref(identity_addr);
    linphone_address_unref(server_addr);
    linphone_account_params_unref(account_params);
    return linphoneData;
}


void callPhone(std::string address, std::string path) {
    std::thread t([address, path]() {
        auto linphoneData = login(path);
        // 发起呼叫（替换目标号码）
        LOGE("linphoneTag 拨打电话开始");
        //---------------------------------------------------------------------
		linphone_core_enable_echo_cancellation(linphoneData.lc, FALSE);
        LinphoneCall *call = linphone_core_invite(linphoneData.lc, address.c_str());
        if (!call) {
            LOGE("linphoneTag 呼叫初始化失败");
            return;
        }
        while (true) {
            linphone_core_iterate(linphoneData.lc);
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
            LinphoneRegistrationState state = linphone_account_get_state(linphoneData.account);
            if (state != LinphoneRegistrationOk) {
                LOGE("linphoneTag 获取状态linphone_account_get_state %d", state);
            }
        }
        LinphoneProxyConfig *proxy_cfg =
            linphone_core_get_default_proxy_config(linphoneData.lc); /* get default proxy config*/
        linphone_proxy_config_edit(proxy_cfg);                       /*start editing proxy configuration*/
        linphone_proxy_config_enable_register(proxy_cfg, FALSE);     /*de-activate registration for this proxy config*/
        linphone_proxy_config_done(proxy_cfg);                       /*initiate REGISTER with expire = 0*/
        while (linphone_proxy_config_get_state(proxy_cfg) != LinphoneRegistrationCleared) {
            linphone_core_iterate(linphoneData.lc); /*to make sure we receive call backs before shutting down*/
            ms_usleep(50000);
        }
        // 清理资源
        linphone_core_remove_account(linphoneData.lc, linphoneData.account);
        linphone_account_unref(linphoneData.account);
        linphone_core_unref(linphoneData.lc);
        linphone_auth_info_unref(linphoneData.auth_info);
    });
    t.detach();
}

void acceptPhone(std::string path) {
    std::thread t([path]() {
        auto linphoneData = login(path);
        while (true) {
            linphone_core_iterate(linphoneData.lc);
            // 检测到来电时允许用户接听
            if (current_call && incoming_call) {
                LOGE("linphoneTag 正在接听来电...");
                linphone_call_accept(current_call);
                incoming_call = false;
            }
            ms_usleep(50000);
        }
        LinphoneProxyConfig *proxy_cfg =
            linphone_core_get_default_proxy_config(linphoneData.lc); /* get default proxy config*/
        linphone_proxy_config_edit(proxy_cfg);                       /*start editing proxy configuration*/
        linphone_proxy_config_enable_register(proxy_cfg, FALSE);     /*de-activate registration for this proxy config*/
        linphone_proxy_config_done(proxy_cfg);                       /*initiate REGISTER with expire = 0*/

        while (linphone_proxy_config_get_state(proxy_cfg) != LinphoneRegistrationCleared) {
            linphone_core_iterate(linphoneData.lc); /*to make sure we receive call backs before shutting down*/
            ms_usleep(50000);
        }
        // 清理资源
        linphone_core_remove_account(linphoneData.lc, linphoneData.account);
        linphone_account_unref(linphoneData.account);
        linphone_core_unref(linphoneData.lc);
        linphone_auth_info_unref(linphoneData.auth_info);
    });
    t.detach();
}


static std::string GetString(napi_env env_, napi_value value) {
    size_t length;
    napi_status status;
    status = napi_get_value_string_utf8(env_, value, nullptr, 0, &length);
    std::string buffer(length, '\0');
    status = napi_get_value_string_utf8(env_, value, &buffer[0], length + 1, &length);
    return buffer;
}

static napi_value CallLinphone(napi_env env, napi_callback_info info) {
    size_t argc = 2;
    napi_value args[2] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    napi_valuetype valuetype0;
    napi_typeof(env, args[0], &valuetype0);
    callPhone(GetString(env, args[0]), GetString(env, args[1]));
    return nullptr;
}

static napi_value AcceptLinphone(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    napi_valuetype valuetype0;
    napi_typeof(env, args[0], &valuetype0);
    acceptPhone(GetString(env, args[0]));
    return nullptr;
}


EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {
        {"callLinphone", nullptr, CallLinphone, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"acceptLinphone", nullptr, AcceptLinphone, nullptr, nullptr, nullptr, napi_default, nullptr}};
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    BuildClass::Export(env, exports);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "entry",
    .nm_priv = ((void *)0),
    .reserved = {0},
};

extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); }
